#include "RDPTB.h"

int Tree_Optimizer (root_t* tree)
{
    ASSERT (tree);

    fprintf (The_Log, "Opt\n");

    while (1)
    {
        int retc = 0, retz = 0;

        retc = Compress_Consts (tree, tree->root);
        retz = Del_Zerones (tree, tree->root);

        if (retc == 0 &&
            retz == 0) break;
    }

    fprintf (The_Log, "/Opt\n");
    return 0;
}

#define CMPRSS_SIGNED_NUMBERS( OP, sign )\
    if (node->left &&\
        (node->left->type == NUMBER) &&\
        node->right == NULL &&\
        node->info == OP)\
    {\
        node->info = sign*node->left->info;\
        node->type = node->left->type;\
        \
        Node_Delete (tree, node->left);\
        \
        node->left = NULL;\
\
        return 1;\
    }

int Compress_Consts (root_t* tree, tree_t* node)
{
    ASSERT (tree); ASSERT (node);

    Tree_Dump (The_Log, tree);

    int retl = 0, retr = 0;

    if (node->left)  retl = Compress_Consts (tree, node->left);
    if (node->right) retr = Compress_Consts (tree, node->right);

    if (node->type == OPERATION)
    {
        if (node->left &&
            node->right &&
            node->left->type  == NUMBER &&
            node->right->type == NUMBER)
        {
            switch ((int) node->info)
            {
                case ADD:
                fprintf (The_Log, "Opt cons ADD\n");
                    node->info = node->left->info + node->right->info;
                    break;

                case SUB:
                fprintf (The_Log, "Opt cons SUB\n");
                    node->info = node->left->info - node->right->info;
                    break;

                case MUL:
                fprintf (The_Log, "Opt cons MUL \n");
                    node->info = node->left->info * node->right->info;
                    break;

                case DIV:
                fprintf (The_Log, "Opt cons DIV\n");
                    node->info = node->left->info / node->right->info;
                    break;

                default:
                    ASSERT (0);
                    break;
            }

            node->type = NUMBER;
            node->left = NULL;
            node->right = NULL;

            return 1;
        }

        if (node->type == OPERATION && node->info == ADD)
        {
            if (node->left && node->right == NULL)
            {
                Tree_Dump (The_Log, tree);
                if (node->parent->left  == node) node->parent->left  = node->left;
                if (node->parent->right == node) node->parent->right = node->left;
                node->left->parent = node->parent;

                node->left   = NULL;
                node->parent = NULL;

                Tree_Dump (The_Log, tree);

//                Node_Destruct (node);

                return 1;
            }

            if (node->right && node->left == NULL)
            {
                Tree_Dump (The_Log, tree);
//                fprintf (The_Log, "AAAA\n");
                if (node->parent->left == node) node->parent->left = node->right;
                if (node->parent->right == node) node->parent->right = node->right;
                node->right->parent = node->parent;

                node->right = NULL;
                node->parent = NULL;

                Tree_Dump (The_Log, tree);

//                Node_Destruct (node);

                return 1;
            }
        }

        CMPRSS_SIGNED_NUMBERS (SUB, -1);
//        CMPRSS_SIGNED_NUMBERS (ADD, 1);
    }

    if (retl != 0 || retr != 0) return 1;

    return 0;
}

#undef CMPRSS_SIGNED_NUMBERS

#define DEL_ZERO_NUM( child_on_check, child_another, num, code)\
    if (node->child_on_check->info == num &&\
        node->child_another->type == NUMBER)\
    {\
        code;\
\
        Node_Delete (tree, node->child_on_check);\
        Node_Delete (tree, node->child_another);\
\
        return 1;\
    }

int Del_Zerones (root_t* tree, tree_t* node)
{
    ASSERT (tree); ASSERT (node);

    Node_Dump (The_Log, node, 0);

    int retl = 0, retr = 0;

    if (node->left)  retl = Del_Zerones (tree, node->left);
    if (node->right) retr = Del_Zerones (tree, node->right);

    if (node->type == OPERATION &&
        node->left &&
        node->right)
    {
        switch ((int) node->info)
        {
            case ADD:
                fprintf (The_Log, "Opt zero ADD\n");
                DEL_ZERO_NUM (left, right, 0, node->info = node->right->info; node->type = node->right->type;);
                DEL_ZERO_NUM (right, left, 0, node->info = node->left->info; node->type = node->left->type;);

                break;

            case SUB:
                fprintf (The_Log, "Opt zero SUB\n");
                DEL_ZERO_NUM (left, right, 0, node->info = (-1)*node->right->info; node->type = node->right->type;);
                DEL_ZERO_NUM (right, left, 0, node->info = node->left->info; node->type = node->left->type;);

                break;

            case MUL:

                fprintf (The_Log, "Opt zero MUL\n");
                if (node->left->info == 0 || node->right->info == 0)
                {
                    node->info = 0;
                    node->type = NUMBER;

                    return 1;
                }

                DEL_ZERO_NUM (left, right, 1, node->info = node->right->info; node->type = node->right->type;);

                DEL_ZERO_NUM (right, left, 1, node->info = node->left->info; node->type = node->left->type;);

                break;

            case DIV:

                fprintf (The_Log, "Opt zero DIV\n");
                DEL_ZERO_NUM (left, right, 0, node->info = 0; node->type = NUMBER;);

                DEL_ZERO_NUM (right, left, 1, node->info = node->left->info; node->type = node->left->type;);

                if (node->right->info == 0)
                {
                    ASSERT (!"Division by zero!!!");
                }

                break;

            default:
                ASSERT (0);
                break;
        }
    }

    if (retl != 0 || retr != 0) return 1;

    return 0;
}

#undef DEL_ZERO_NUM
